package service

import (
	"bufio"
	"crypto/md5"
	"errors"
	"fmt"
	"mime/multipart"
	"oss-api/app/dto"
	"oss-api/app/model"
	"oss-api/boot"
	"oss-api/library/request"
	"oss-api/library/response"
	"strconv"
	"strings"
	"time"
)

var Resource = new(ResourceService)

type ResourceService struct{}

// buildFileName 构建新的文件名称
func buildFileName(pathName, name string) string {
	now := time.Now()
	split := strings.Split(name, ".")
	name = fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("%d%s", time.Now().Unix(), split[0]))))
	return fmt.Sprintf("%s/%s/%s.%s", pathName, now.Format("2006-01-02"), name, split[1])
}

func (r *ResourceService) cateGoryToMap(id ...int64) (map[int64]string, error) {
	var (
		data   []response.CategorySimpleVo
		err    error
		result map[int64]string
	)

	if len(id) <= 0 {
		data, err = dto.Category.FindCondition("can_view = true and id in (?)", id)
	} else {
		data, err = dto.Category.FindCondition("can_view = true")
	}

	if err != nil {
		return nil, err
	}

	result = make(map[int64]string, len(data))

	for i := 0; i < len(data); i++ {
		result[data[i].Id] = data[i].PathName
	}
	return result, nil
}

// UploadFile 上传文件
func (r *ResourceService) UploadFile(file *multipart.FileHeader, tags string, cateId int64, brief string) (model.Resource, error) {
	var (
		category model.Category
		err      error
		url      string
	)
	category, err = dto.Category.FindByCateId(cateId)
	if err != nil {
		fmt.Printf("[oss-api] %s [INFO] : 获取分类信息失败：%s\n", time.Now().Format("2006-01-02 15:04:05"), err.Error())
		return model.Resource{}, err
	}
	path := buildFileName(category.PathName, file.Filename)
	fmt.Printf("[oss-api] %s [INFO] : 上传路径：%s\n", time.Now().Format("2006-01-02"), path)
	open, _ := file.Open()
	defer open.Close()
	url, err = dto.Storage.UploadFile(path, bufio.NewReader(open))
	// 创建记录
	return dto.Resource.CreateResource(category.Id, file.Size, path, file.Filename, url, tags, brief)
}

// ResourcePage 分页查询资源信息
func (r *ResourceService) ResourcePage(param request.ResourcePageVo) (response.ResultPage, error) {
	count, err := dto.Resource.Count("can_view = true and cate_id = ?", param.CateId)
	if err != nil {
		return response.ResultPage{}, err
	}
	result, err := dto.Resource.SelectPage(param.CateId, param.Page, param.Limit)
	if err != nil {
		return response.ResultPage{}, err
	}

	endpoint := boot.Config.Storage.Cloud.Endpoint

	for i := 0; i < len(result); i++ {
		result[i].OriginalUrl = fmt.Sprintf("https://%s/%s", endpoint, result[i].OriginalUrl)
		result[i].HandleUrl = fmt.Sprintf("https://%s/%s", endpoint, result[i].HandleUrl)
	}

	return response.ResultPage{
		Page:    param.Page,
		Limit:   param.Limit,
		Total:   count,
		Content: result,
	}, nil
}

// transformationType 类型转换
func transformationType(catId, ids string) (int64, []int64, error) {
	var (
		cate int64
		id   []int64
		err  error
	)
	cate, err = strconv.ParseInt(catId, 10, 64)
	if err != nil {
		return 0, nil, err
	}
	split := strings.Split(ids, ",")
	id = make([]int64, len(split))
	for i := 0; i < len(split); i++ {
		id[i], err = strconv.ParseInt(split[i], 10, 64)
		if err != nil {
			return 0, nil, err
		}
	}
	return cate, id, nil
}

// DeleteResource 删除资源
func (r *ResourceService) DeleteResource(cate, ids string) (bool, error) {
	if cate == "" || ids == "" {
		return false, errors.New("参数丢失")
	}
	_, resourceIds, err := transformationType(cate, ids)
	if err != nil {
		return false, errors.New("请求参数类型错误")
	}
	// TODO 需要设置为私有权限
	return dto.Resource.DeleteResourceByIds(resourceIds, false)
}

// GetGarbageResource 获取回收站的资源列表
func (r *ResourceService) GetGarbageResource(params request.GarbagePageVo) (response.ResultPage, error) {
	var (
		cates   []response.CategorySimpleVo
		cateMap map[int64]string
		data    []response.GarbagePageVo
		count   int64
		err     error
	)

	cates, err = dto.Category.FindAll()
	if err != nil {
		return response.ResultPage{}, err
	}

	cateMap = make(map[int64]string, len(cates))

	for i := 0; i < len(cates); i++ {
		cateMap[cates[i].Id] = cates[i].Title
	}

	data, count, err = dto.Resource.SelectPageWithDelete(params.Page, params.Limit, params.CateId, params.Name)
	if err != nil {
		return response.ResultPage{}, err
	}
	return response.ResultPage{
		Page:    params.Page,
		Limit:   params.Limit,
		Content: data,
		Total:   count,
	}, nil
}

func stringToInt64(target string) ([]int64, error) {
	split := strings.Split(target, ",")
	resIds := make([]int64, len(split))
	for i := 0; i < len(split); i++ {
		parseInt, err := strconv.ParseInt(split[i], 10, 64)
		if err != nil {
			return nil, err
		}
		resIds[i] = parseInt
	}
	return resIds, nil
}

// ResourceReduction 资源还原
func (r *ResourceService) ResourceReduction(ids string) (bool, error) {
	idInt64s, err := stringToInt64(ids)
	if err != nil {
		return false, err
	}
	return dto.Resource.DeleteResourceByIds(idInt64s, true)
}

// ThoroughDelete 彻底清理资源
func (r *ResourceService) ThoroughDelete(ids string) (bool, error) {
	var (
		resource     []model.Resource
		err          error
		idInt64s     []int64
		resourcePath []string
		delIds       []int64
	)
	if ids == "all" {
		// 请求全部
		resource, err = dto.Resource.FindAllStatusDelete("can_view = false")
	} else {
		idInt64s, _ = stringToInt64(ids)
		resource, err = dto.Resource.FindAllStatusDelete("can_view = false and id in (?)", idInt64s)
	}
	if err != nil {
		return false, err
	}
	if len(resource) <= 0 {
		return true, nil
	}
	// 转换存储路径
	resourcePath = make([]string, len(resource))
	delIds = make([]int64, len(resource))
	for idx, item := range resource {
		resourcePath[idx] = item.OriginalUrl
		delIds[idx] = item.Id
	}
	_ = dto.Storage.DeleteResource(resourcePath...)
	// 删除记录
	return dto.Resource.RealDelete(delIds...)
}

// SetResourcePerm 设置访问权限
func (r *ResourceService) SetResourcePerm(id int64, state bool) (bool, error) {
	var (
		data model.Resource
		err  error
	)
	data, err = dto.Resource.FindOne("id = ? and can_view = true", id)
	if err != nil {
		return false, err
	}
	if data.AccessPerm == state {
		return true, nil
	}
	err = dto.Storage.SetResourcePerm(data.OriginalUrl, state)
	if err != nil {
		return false, err
	}
	return dto.Resource.SetResourceVisit(id, state)
}

// Tags 获取所有资源的标签
func (r *ResourceService) Tags() ([]string, error) {
	return dto.Resource.FindTags()
}
